﻿//
// Created by Administrator on 2022/6/26.
//
#define WIN32_LEAN_AND_MEAN

#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include <cstdio>
#include <winsock2.h>
#include <cstring>
#include <string>
#include <iostream>
#include <stack>
#include <action_event.h>
#include <utils.h>
#include "HostDetailFrame.h"
#include "ids.h"
#include "ChatPanel.h"

#define BUFF_LEN 65536
#define CHAT_MAX_LEN 768

extern int touch_server_port;
extern string touch_server_ip;
extern int message_delay;

BEGIN_EVENT_TABLE ( ClientDetailFrame, wxFrame )
                EVT_MENU ( ACTION_EVENT, ClientDetailFrame::OnActionEvent )
                EVT_CLOSE(ClientDetailFrame::OnClose )
END_EVENT_TABLE()

class MessageThread: public wxThread
{
    friend class ClientDetailFrame;
    char recvBuf[BUFF_LEN];
    //char chat[CHAT_MAX_LEN];
    //bool posted;
    wxWindow * parent;
    wxString *s;
    int udp_loop();
    //volatile bool loopIt;
    StringBuffer GetChatHistory();
    std::vector< shared_ptr<GetChat>> chats;
    //wxMutex m_mutex;
    wxCriticalSection m_pThreadCS;
    SOCKET sock_Client; //客户端用于通信的Socket
    wxString from_host;
    wxString to_host;
    bool first;
public:
    MessageThread ( wxWindow * parent, const wxString& from_host, const wxString& to_host );
    void * Entry() override;
    ~MessageThread() override;
};


MessageThread::MessageThread ( wxWindow * parent, const wxString& from_host,
                     const wxString& to_host ){
    this->parent = parent;
    this->from_host = from_host;
    this->to_host = to_host;
    first = true;
}

void * MessageThread::Entry(){
    udp_loop( );
    return nullptr;
}

MessageThread::~MessageThread(){
    closesocket(sock_Client);
    //WSACleanup();
    ActionEvent event ( parent, TOKEN_UDP_LOOP_END, nullptr );
    event.Post();
}

StringBuffer MessageThread::GetChatHistory() {
    //query records from static_hostname->GetValue, to MyHostname
    Document document;
    document.SetObject();
    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();

    //wxMutexLocker lock(m_mutex);
    m_pThreadCS.Enter();
    if (!chats.empty()) {
        shared_ptr<GetChat> chat = chats.front();
        GetChat *p = chat.get();
        document.AddMember( key_request_post, StringRef( key_request_post_value_chat ), allocator);
        //wxString w( chat->datetime  );
        //const char* _s = w.mb_str( wxConvUTF8 );
        document.AddMember( key_request_chat_datetime, StringRef(p->datetime), allocator);
        document.AddMember( key_request_chat_from_host, StringRef( chat->from_host ), allocator );
        //wxString to( chat->to  );
        //_s = to.mb_str( wxConvUTF8 );
        document.AddMember( key_request_chat_to_host, StringRef(chat->to), allocator);
        document.AddMember( key_request_chat_content, StringRef(chat->chat), allocator);
    }

    document.AddMember( key_resp_key_get, StringRef( key_request_get_value_chat_history ), allocator);
    /*
    WCHAR host_name[256];
    DWORD _size;
    GetComputerName(host_name, &_size);
    wprintf(L"%s\n", host_name);

    const char* w = wxString(host_name).mb_str(wxConvUTF8);*/
    const char* w = to_host.mb_str(wxConvUTF8);
    document.AddMember( key_request_hostname, StringRef( w ), allocator );

    document.AddMember( key_request_chat_to_host, StringRef( w ), allocator );

    w = from_host.mb_str(wxConvUTF8);
    document.AddMember( key_request_chat_from_host, StringRef( w ), allocator );

    if( first ){
        document.AddMember( "frame_on_load", first, allocator );
        first = false;
    }else{
        document.AddMember( "frame_on_load", first, allocator );
    }
    StringBuffer stringBuffer;
    Writer<StringBuffer> writer(stringBuffer);
    document.Accept(writer);

    if (!chats.empty())
        chats.erase(chats.begin());
    m_pThreadCS.Leave();
    return stringBuffer;
}

int MessageThread::udp_loop(){


    sock_Client = socket( AF_INET,SOCK_DGRAM,IPPROTO_UDP );//创建客户端用于通信的Socket

    //SetNonBlocking( sock_Client );
    SOCKADDR_IN addr_server;   //服务器的地址数据结构
    addr_server.sin_family = AF_INET;
    addr_server.sin_port = htons( touch_server_port );//端口号为4567
    addr_server.sin_addr.S_un.S_addr = inet_addr( touch_server_ip.c_str() );
    SOCKADDR_IN sock;

    int len = sizeof(sock);
    //!TestDestroy，没发生请求退出的消息
    while( !TestDestroy() ){
        //Document document;
        StringBuffer stringBuffer = GetChatHistory(  );

        //
        fprintf( stderr, "POST( function:%s ): \n%s\n", __FUNCTION__, stringBuffer.GetString() );

        sendto( sock_Client, stringBuffer.GetString(), stringBuffer.GetSize(),
                0,(SOCKADDR*)&addr_server, sizeof(SOCKADDR) );

        memset( recvBuf, 0, BUFF_LEN );
        int err = recvfrom( sock_Client, recvBuf, BUFF_LEN,
                             0, (SOCKADDR*)&sock, &len );
        if( err>0 ){
            fprintf( stderr, "Response: function( %s ), line( %d ), received( %d )\n%s\n",
                    __FUNCTION__ , __LINE__, err, recvBuf );
        }
        if( parent!= nullptr ) {
            ActionEvent event(parent, TOKEN_UDP_MESSAGE_RESPONSE, recvBuf);
            event.Post();
        }
        Sleep( message_delay );
    }

    return 0;
}


//panel刷新，这个frame竟然会消失。parent
ClientDetailFrame::ClientDetailFrame( wxWindow* parent, GetHostInfo* hostInfo, wxWindowID id,
        const wxString& title, const wxPoint& pos, const wxSize& size, long style )
        : wxFrame(parent, id, title, pos, size, style )    //wxFrame不能设置为parent，与HostInfoPanel绑定，HostInfoPanel会定时销毁
{
    SetLabel( hostInfo->hostname );
    this->hostInfo = hostInfo;
    this->SetSizeHints( wxDefaultSize, wxDefaultSize );
    ToggleWindowStyle (wxSTAY_ON_TOP);

    wxBoxSizer* sizer_client;
    sizer_client = new wxBoxSizer( wxVERTICAL );

    wxStaticBoxSizer* sizer_client_detail_info;
    sizer_client_detail_info = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxT("Detail") ),
            wxVERTICAL );

    wxBoxSizer* sizer_host;
    sizer_host = new wxBoxSizer( wxHORIZONTAL );

    static_hostname = new wxStaticText( sizer_client_detail_info->GetStaticBox(),
            wxID_ANY, wxString::FromUTF8( hostInfo->hostname ),
            wxDefaultPosition, wxDefaultSize, 0 );
    static_hostname->Wrap( -1 );
    sizer_host->Add( static_hostname, 0, wxALL, 5 );

    static_datetime = new wxStaticText( sizer_client_detail_info->GetStaticBox(), wxID_ANY,
            wxString::FromUTF8( hostInfo->datetime ),
            wxDefaultPosition, wxDefaultSize, 0 );
    static_datetime->Wrap( -1 );
    sizer_host->Add( static_datetime, 0, wxALL, 5 );

    static_wan_ip = new wxStaticText(sizer_client_detail_info->GetStaticBox(), wxID_ANY, hostInfo->wan_ip,
                                     wxDefaultPosition, wxDefaultSize, 0 );
    static_wan_ip->Wrap(-1 );
    sizer_host->Add( static_wan_ip, 0, wxALL, 5 );

    sizer_client_detail_info->Add( sizer_host, 0, wxEXPAND, 5 );

    wxBoxSizer* sizer_interfaces;
    sizer_interfaces = new wxBoxSizer( wxVERTICAL );

    //wxBoxSizer* sizer_interface;
    //sizer_interface = new wxBoxSizer( wxHORIZONTAL );

    static_system = new wxStaticText(sizer_client_detail_info->GetStaticBox(), wxID_ANY,
            wxString::FromUTF8( hostInfo->system ),
                                     wxDefaultPosition, wxDefaultSize, 0 );
    static_system->Wrap(-1 );
    sizer_client_detail_info->Add( static_system, 0, wxALL, 5 );

    static_client_install_dir = new wxStaticText(sizer_client_detail_info->GetStaticBox(), wxID_ANY,
            wxString::FromUTF8( hostInfo->client_install_dir ),
                                                 wxDefaultPosition, wxDefaultSize, 0 );
    static_client_install_dir->Wrap(-1 );
    sizer_client_detail_info->Add(static_client_install_dir, 0, wxALL, 5 );

    /*
    static_interfaces = new wxStaticText(sizer_client_detail_info->GetStaticBox(), wxID_ANY,
                                         wxString::FromUTF8( hostInfo->interfaces ),
                                         wxDefaultPosition, wxDefaultSize, 0 );*/
    static_interfaces = new wxTextCtrl( sizer_client_detail_info->GetStaticBox(),
            wxID_ANY, wxString::FromUTF8( hostInfo->interfaces ),
                    wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE|wxTE_READONLY );
    //static_interfaces->Wrap(-1 );
    sizer_client_detail_info->Add(static_interfaces, 0, wxALL|wxEXPAND, 5 );

    //sizer_interfaces->Add( sizer_interface, 1, wxEXPAND, 5 );

    sizer_client_detail_info->Add( sizer_interfaces, 1, wxEXPAND, 5 );

    sizer_client->Add( sizer_client_detail_info, 0, wxEXPAND, 5 );

    wxStaticBoxSizer* sizer_chat_frame;
    sizer_chat_frame = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxT("CHAT") ), wxVERTICAL );

    sizer_chat_frame->SetMinSize( wxSize( -1,100 ) );
    scrolled_chats = new wxScrolledWindow(sizer_chat_frame->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize,
            wxHSCROLL|wxVSCROLL );
    scrolled_chats->SetScrollRate(5, 5 );

    sizer_chats = new wxBoxSizer(wxVERTICAL );
    scrolled_chats->AlwaysShowScrollbars();

    scrolled_chats->SetSizer(sizer_chats );
    scrolled_chats->Layout();
    sizer_chats->Fit( scrolled_chats );
    sizer_chat_frame->Add(scrolled_chats, 6, wxEXPAND | wxALL, 5 );

    wxBoxSizer* sizer_chat;
    sizer_chat = new wxBoxSizer( wxHORIZONTAL );

    text_message = new wxTextCtrl( sizer_chat_frame->GetStaticBox(), wxID_ANY, wxEmptyString,
            wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE );
    sizer_chat->Add( text_message, 1, wxALL|wxEXPAND, 5 );

    button_post = new wxButton( sizer_chat_frame->GetStaticBox(), wxID_ANY, wxT("发送"),
            wxDefaultPosition, wxDefaultSize, 0 );
    button_post->SetFont( wxFont( 24, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
            wxFONTWEIGHT_BOLD, false, wxT("楷体") ) );
    button_post->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );

    sizer_chat->Add( button_post, 0, wxALL|wxEXPAND, 5 );

    sizer_chat_frame->Add( sizer_chat, 1, wxEXPAND, 5 );

    sizer_client->Add( sizer_chat_frame, 1, wxEXPAND, 3 );

    this->SetSizer( sizer_client );
    this->Layout();

    this->Centre( wxBOTH );
    workThread = new MessageThread( this,  GetLabel(), wxGetHostName() );
    workThread->Run();
    button_post->Connect( wxEVT_LEFT_DOWN,
                               wxMouseEventHandler(ClientDetailFrame::OnPost ), nullptr, this );
}

void ClientDetailFrame::OnPost( wxMouseEvent &event ){
    if( text_message->IsEmpty() ) {
        wxMessageBox("不能为空", "错误", wxOK | wxCENTRE, this);
        return;
    }
    if( text_message->GetLabel().length()> CHAT_MAX_LEN ){
        wxString tmp;
        tmp.sprintf("消息长度%d, 不能长于%d", text_message->GetLabel().length(), CHAT_MAX_LEN );
        wxMessageBox( tmp, "错误", wxOK | wxCENTRE, this);
        return;
    }
    char datetime[64];
    GetNowTime( datetime );

    WCHAR host_name[256];
    DWORD _size;
    GetComputerName( host_name, &_size );
    wprintf( L"%s\n", host_name );

    auto _chat = make_shared<GetChat>( datetime,
            wxString( host_name ).mb_str( wxConvUTF8 ), //the_host
            static_hostname->GetLabel().mb_str( wxConvUTF8 ),
            text_message->GetValue().mb_str( wxConvUTF8 ) );
    workThread->m_pThreadCS.Enter();
    workThread->chats.push_back( _chat );
    text_message->SetLabel( "" );
    workThread->m_pThreadCS.Leave();
}

//workThread destroy
ClientDetailFrame::~ClientDetailFrame(){
   // workThread->m_pThreadCS.Leave();
   // workThread->loopIt = false;
    //workThread->Delete();
    //workThread->Exit();
    workThread->Delete();
    //delete workThread;
}

void ClientDetailFrame::BuildChatItemView(GetChat* chat ) {
    sizer_chats->Add( new ChatPanel( scrolled_chats, chat->from_host,
            chat->datetime, chat->chat ) );
    //scrolled_clients_window->SetSizer( scroll_window_sizer );
    sizer_chats->Layout();
    sizer_chats->Fit( scrolled_chats );
    //scrolled_chats->Layout();
    //sizer_chats->Fit( scrolled_chats );
    scrolled_chats->Fit();
    //OnResize
    //wxSize size = scrolled_chats->GetBestVirtualSize();
    //scrolled_chats->SetVirtualSize( size );
}

void ClientDetailFrame::OnActionEvent( wxCommandEvent &event ){
    const int token = event.GetInt();
    const long liType = event.GetExtraLong();
    char datetime[256];
    switch ( token ) {
        case TOKEN_UDP_MESSAGE_RESPONSE:{
            GetNowTime( datetime );
            auto* recv_buf = ( const char* )event.GetClientData();
            rapidjson::Document doc;
            bool r = doc.Parse( recv_buf ).HasParseError();
            if( !r ){
                if( doc.HasMember( key_request_get ) && doc[key_request_get].IsObject() ) {
                    if ( doc[key_request_get].HasMember("hosts") && doc[key_request_get]["hosts"].IsArray()) {
                        Value &hostsNode = doc[key_request_get]["hosts"];
                        int d = hostsNode.Size();

                            /*
                            */

                    }else if ( doc[key_request_get].HasMember( key_resp_chats )
                               && doc[key_request_get][ key_resp_chats ].IsArray()  ){
                        Value &_chats = doc[key_request_get][key_resp_chats];
                        //scrolled_chats->DestroyChildren();
                        wxVector< std::shared_ptr<GetChat> > tmp;
                        get_chats.swap( tmp );
                        for( rapidjson::SizeType i=0; i< _chats.Size(); i++ ) {
                            shared_ptr<GetChat> chat( new GetChat( _chats[i][key_resp_chat_datetime].GetString(),
                                                                   _chats[i][key_resp_chat_from_host].GetString(),
                                                                   _chats[i][key_resp_chat_to_host].GetString(),
                                                                   _chats[i][key_resp_chat_content].GetString() ));
                            get_chats.push_back( chat );
                            BuildChatItemView(chat.get());
                        }
                    }
                }
            }
            break;
        }
        case TOKEN_UDP_LOOP_END:{
            //之前的线程已退出，可以重起新线程
            wxMessageBox( "线程已退出");
            delete workThread;
            break;
        }
    }
}

void ClientDetailFrame::OnClose( wxCloseEvent& _event ){
    //ActionEvent event ( parent, TOKEN_UDP_LOOP_END, nullptr );
    //event.Post();
    this->Destroy();
}